﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
// qt headers
#include <QColor>
#include <vector>
#include <QIcon>
#include <QImage>
#include <memory>
#include <QPixmap>
#include <QFileInfo>

//custom headers
#include "ProjectCloudComponentService.h"
#include "IGmepGcsProjectManager.h"
#include "UrlImageProvider.h"
#include "PathUtils.h"
#include "AppService.h"
#include "ProjectLoadedComponentService.h"
#include "ProjectComponentsListModel.h"
#include "GmepLocalDbService.h"


#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 
namespace gmepgcs
{
    ListItemModel::ListItemModel(QObject *parent) : QAbstractItemModel(parent),
        m_Components(),
        m_spUrlThumbnailProvider(std::make_shared<UrlImageProvider>()),
        m_mpCheckSates()
    {
        connect(m_spUrlThumbnailProvider.get(), &UrlImageProvider::OnProvided, this, &ListItemModel::UpdateThumbnail);
    }

    ListItemModel::~ListItemModel()
    {
    }

    int ListItemModel::rowCount(const QModelIndex &parent) const
    {
        if (parent.isValid())
        {
            return 0;
        }

        return (int)m_Components.size();
    }

    int ListItemModel::columnCount(const QModelIndex &parent) const
    {
        return 1;
    }

    QVariant ListItemModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid())
        {
            return QVariant();
        }

        if (index.row() >= m_Components.size())
        {
            return QVariant();
        }

        IProjectComponent *component = static_cast<IProjectComponent*>(index.internalPointer());
        if (!component)
        {
            return QVariant();
        }

        gcmp::IDocument* pDoc = AppService::GetCurrentDocument();
        switch (role) {
        case Qt::SizeHintRole:
        {
            return QSize(128, 136);
            break;
        }
        case Qt::DisplayRole:
        {
            QString displayName = QString::fromStdWString(component->GetDisplayName());
            if (displayName.size() > 10)
            {
                displayName = displayName.left(10) + "...";
            }
            return displayName;
        }
        case Qt::UserRole:
        {

                if (!component->IsReleased())
                {
                    return 0;
                }
                

                if (!ProjectLoadedComponentService::Get()->IsComponentLoadedByCloudId(pDoc, component->Id))
                {
                    return 1;
                }
                return 0;

        }
        case Qt::TextAlignmentRole:
            return (int)Qt::AlignHCenter | Qt::AlignTop;
        case Qt::TextColorRole:
            return QColor("#3B3B3B");
        case Qt::BackgroundColorRole:
            return QColor("#FFFFFF");
        case Qt::DecorationRole:
        {
            if (component->IsProcessing())
            {
                return QIcon(":/images/pic_component_thumbnail_processing.png");
            }

            if (component->IsProcessedFailed())
            {
                return QIcon(":/images/pic_component_thumbnail_process_failed.png");
            }

            std::wstring url = component->Thumbnail;
            QString filePath = QString::fromStdWString(url);
            QFileInfo fileInfo(filePath);
            if (fileInfo.exists()) //本地
            {
                return QIcon(filePath);
            }
            else
            {
                filePath = QString::fromStdWString(PathUtils::GetThumbnailDownloadFilePath(url, L".png"));
                fileInfo.setFile(filePath);
                if (fileInfo.exists())
                {
                    return QIcon(filePath);
                }
                else
                {
                    DBG_WARN_AND_RETURN_UNLESS(m_spUrlThumbnailProvider, QVariant(), L"m_spUrlThumbnailProvider为空", L"GMEP", L"2024-01-30");
                    m_spUrlThumbnailProvider->Provide(url);
                    return QIcon(":/images/default_thumbnail.png");
                }
            }
        }
        case Qt::CheckStateRole:
        {
            return m_mpCheckSates[component->Id];
        }
        case Qt::ToolTipRole:
        {
            return QString::fromStdWString(component->GetDisplayName());
        }
        default:
            break;
        }
        return QVariant();
    }

    QModelIndex ListItemModel::index(int row, int column, const QModelIndex &parent) const
    {
        if (hasIndex(row, column, parent) && row < m_Components.size() && column < 1)
        {
            //IProjectComponent *component = const_cast<IProjectComponent*>(m_Components[row].get());
            DBG_WARN_AND_RETURN_UNLESS(row >= 0, QModelIndex(), L"row为空", L"GMEP", L"2024-01-30");
            DBG_WARN_AND_RETURN_UNLESS(m_Components[row], QModelIndex(), L"m_Components[row]为空", L"GMEP", L"2024-01-30");
            return createIndex(row, column, m_Components[row].get());
        }

        return QModelIndex();
    }

    QModelIndex ListItemModel::parent(const QModelIndex &child) const
    {
        return QModelIndex();
    }
    
    bool ListItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if (!index.isValid())
            return false;

        IProjectComponent *component = static_cast<IProjectComponent *>(index.internalPointer());
        if (!component)
        {
            return false;
        }

        switch (role)
        {
        case Qt::CheckStateRole:
        {
            if (index.column() == 0)
            {
                IProjectComponent *pSavedComponent = NEW_AS_QT(IProjectComponent);
                DBG_WARN_AND_RETURN_FALSE_UNLESS(pSavedComponent, L"pSavedComponent为空", L"GMEP", L"2024-01-30");
                pSavedComponent->Id = component->Id;
                pSavedComponent->Name = component->Name;
                pSavedComponent->Version = component->Version;
                pSavedComponent->Source = component->Source;
                pSavedComponent->Status = component->Status;
                if (value.toBool())
                {
                    if (!m_ReadyToOperationList.contains(pSavedComponent->Id))
                    {
                        m_ReadyToOperationList.insert(pSavedComponent->Id, pSavedComponent);
                    }

                }
                else
                {
                    if (m_ReadyToOperationList.contains(pSavedComponent->Id))
                    {
                        m_ReadyToOperationList.remove(pSavedComponent->Id);
                    }

                }
                if(m_mpCheckSates.contains(component->Id))
                {
                    bool boolValue = value.toBool();
                    m_mpCheckSates[component->Id] = boolValue;
                    emit dataChanged(index, index, { role });

                    OnItemCheckStateChanged();
                    emit ComponentCheckStateChanged(component, boolValue);
                    return true;
                }
                return false;
            }
        }

        default:
            return QAbstractItemModel::setData(index, value, role);
        }

        emit dataChanged(index, index);

        return QAbstractItemModel::setData(index, value, role);
    }
    
    void ListItemModel::ChangeCheckStates(Qt::CheckState state)
    {
        if (state == Qt::CheckState::PartiallyChecked)
        {
            return;
        }

        QModelIndex index;
        for (int32_t i = 0; i != rowCount(); ++i)
        {
            index = this->index(i, 0);
            bool oldValue = data(index, Qt::CheckStateRole).toBool();
            bool newValue = state == Qt::CheckState::Checked;
            if (oldValue != newValue)
            {
                setData(index, newValue, Qt::CheckStateRole);
            }
        }
    }

    void ListItemModel::ChangeCheckState(IProjectComponent *compoenent, bool checked)
    {
        if (!compoenent)
        {
            return;
        }

        for (int i = 0; i < rowCount(); ++i)
        {
            auto index = this->index(i, 0);
            if (!index.isValid())
            {
                continue;
            }

            auto pComponent = static_cast<IProjectComponent*>(index.internalPointer());
            if (!pComponent)
            {
                continue;
            }

            if (pComponent->Id == compoenent->Id)
            {
                auto oldValue = data(index, Qt::ItemDataRole::CheckStateRole).toBool();
                if (oldValue != checked)
                {
                    setData(index, checked, { Qt::ItemDataRole::CheckStateRole });
                }
                return;
            }
        }
    }

    void ListItemModel::OnItemCheckStateChanged()
    {
        int32_t checkedCount = 0;
        for (auto &item : m_mpCheckSates)
        {
            if (item)
            {
                ++checkedCount;
            }
        }

        Qt::CheckState state = Qt::CheckState::Unchecked;
        if (checkedCount >= m_mpCheckSates.count())
        {
            state = Qt::CheckState::Checked;
        }
        else if (checkedCount > 0)
        {
            state = Qt::CheckState::PartiallyChecked;
        }

        emit CheckStatesChanged(state, checkedCount);
    }

    void ListItemModel::SetComponents(std::vector<std::shared_ptr<IProjectComponent>> components)
    {
        int32_t oldRowCount = rowCount();
        if (oldRowCount > 0)
        {
            beginRemoveRows(QModelIndex(), 0, oldRowCount - 1);
            removeRows(0, oldRowCount);
            endRemoveRows();
        }

        m_Components = components;
        m_mpCheckSates.clear();
        for (auto &item : m_Components)
        {
            if (item == nullptr)
            {
                continue;
            }
            m_mpCheckSates[item->Id] = false;
        }
        emit CheckStatesChanged(Qt::CheckState::Unchecked, 0);
        
        int32_t newRowCount = (int32_t)m_Components.size();
        if (newRowCount > 0)
        {
            beginInsertRows(QModelIndex(), 0, newRowCount - 1);
            insertRows(0, newRowCount);
            endInsertRows();
        }

        emit dataChanged(createIndex(0, 0), createIndex(newRowCount - 1, columnCount() - 1));
        emit ListUpdated();
    }

    void ListItemModel::UpdateThumbnail(std::wstring imageKey, std::shared_ptr<QImage> spImage)
    {
        int32_t rowCount = this->rowCount();
        for (int32_t i = 0; i != rowCount; ++i)
        {
            QModelIndex index = this->index(i, 0, QModelIndex());
            if (index.isValid())
            {
                IProjectComponent *pComponent = static_cast<IProjectComponent *>(index.internalPointer());
                if (pComponent)
                {
                    std::wstring url = pComponent->Thumbnail;
                    if (url == imageKey)
                    {
                        if (spImage)
                        {
                            emit dataChanged(index, index, { Qt::DecorationRole });
                        }

                        return;
                    }
                }
            }
        }
    }

    void ListItemModel::RefreshComponent(const std::shared_ptr<IDetailedProjectComponent> detailedProjectComponent)
    {
        if (!detailedProjectComponent)
        {
            return;
        }

        for (int i = 0; i != rowCount(); ++i)
        {
            auto index = this->index(i, 0);
            if (index.isValid())
            {
                IProjectComponent *component = static_cast<IProjectComponent *>(index.internalPointer());
                if (component && component->Id == detailedProjectComponent->Id)
                {
                    component->Name = detailedProjectComponent->Name;
                    auto thumbnail = std::wstring();
                    std::wstring thumbnailSize = L"256";
                    std::wstring thumbnailAngle = L"0";
                    auto thumbnailSizeGroupIterator = detailedProjectComponent->Thumbnails.find(thumbnailSize);
                    if (thumbnailSizeGroupIterator != detailedProjectComponent->Thumbnails.end())
                    {
                        auto thumbnailName = QString::fromStdWString(thumbnailSize + L"_" + thumbnailAngle);
                        auto thumbnailSizeGroup = thumbnailSizeGroupIterator->second;
                        auto thumbnailIterator = std::find_if(thumbnailSizeGroup.begin(), thumbnailSizeGroup.end(),
                            [thumbnailName](const std::vector<std::wstring>::value_type &thumb) {
                            QFileInfo fileInfo(QString::fromStdWString(thumb));
                            return fileInfo.completeBaseName() == thumbnailName;
                        });

                        if (thumbnailIterator != thumbnailSizeGroup.end())
                        {
                            thumbnail = *thumbnailIterator;
                        }
                        else if (thumbnailSizeGroup.begin() != thumbnailSizeGroup.end())
                        {
                            thumbnail = *thumbnailSizeGroup.begin();
                        }
                    }
                    component->Thumbnail = thumbnail;
                    component->Version = detailedProjectComponent->Version;
                    component->SetIsSyncable(detailedProjectComponent->GetIsSyncable());
                    component->SetIsUpdatable(detailedProjectComponent->GetIsUpdatable());
                    component->SetVersionInLocalDb(detailedProjectComponent->GetVersionInLocalDb());
                    component->SetVersionInDocument(detailedProjectComponent->GetVersionInDocument());
                    dataChanged(this->index(i, 0), this->index(i, 0), { Qt::DisplayRole | Qt::DecorationRole | Qt::UserRole});

                    break;
                }
            }
        }
    }
}
